// Fill out your copyright notice in the Description page of Project Settings.
PRAGMA_DISABLE_OPTIMIZATION

#include "InteractionInteractorComponent.h"

#include "AnimSampleGameInstance.h"
#include "AsyncTreeDifferences.h"
#include "InteractionParticipantComponent.h"

// Sets default values for this component's properties
UInteractionInteractorComponent::UInteractionInteractorComponent()
{
	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
	// off to improve performance if you don't need them.
	PrimaryComponentTick.bCanEverTick = true;

	// ...
}


// Called when the game starts
void UInteractionInteractorComponent::BeginPlay()
{
	Super::BeginPlay();
	UAnimSampleGameInstance* GameInstance = (UAnimSampleGameInstance*)GetWorld()->GetGameInstance();
	if(GameInstance != nullptr)
	{
		GameInstance->RegisterInteractionScriptFunctionByName(this, "NotifyInteractorStartScript", StartScriptFunctionName);
		GameInstance->RegisterInteractionScriptFunctionByName(this, "NotifyInteractorStopScript", StopScriptFunctionName);
	}
}


// Called every frame
void UInteractionInteractorComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
	// Do that just on local client
	if(!(GetOwnerRole() == ROLE_AutonomousProxy || GetNetMode() == ENetMode::NM_ListenServer || GetNetMode() == NM_Standalone))
	{
		return;
	}
	FHitResult HitResult;
	RaycastInterestActor(HitResult, DeltaTime);
	AActor* HitActor = HitResult.GetActor();
	if(HitActor == nullptr)
	{
		InterestActor = nullptr;
		return;
	}
	UInteractionParticipantComponent* participant = HitActor->GetComponentByClass<UInteractionParticipantComponent>();
	if(participant != nullptr)
	{
		InterestActor = HitActor;
	}
	else
	{
		InterestActor = nullptr;
	}
}

void UInteractionInteractorComponent::RaycastInterestActor(FHitResult& HitResult, float DeltaTime)
{
	AActor* Owner = GetOwner();
	APawn* Pawn = Cast<APawn>(Owner);
	if(Pawn == nullptr)
	{
		return;
	}
	APlayerController* PlayerController = Cast<APlayerController>(Pawn->GetController());
	if(PlayerController == nullptr)
	{
		return;
	}
	FVector StartLocation = FVector::Zero();
	FRotator CameraRotator = FRotator::ZeroRotator;
	PlayerController->GetPlayerViewPoint(StartLocation, CameraRotator);
	FVector Direction = CameraRotator.Vector();
	FVector EndLocation = StartLocation + Direction * InteractiveDistance;
	StartLocation = StartLocation + Direction * 2;
	GetWorld()->LineTraceSingleByChannel(HitResult, StartLocation, EndLocation, ECC_WorldDynamic);

	// if (HitResult.bBlockingHit)
	// {
	// 	DrawDebugLine(GetWorld(), StartLocation, HitResult.ImpactPoint, FColor::Green, false, DeltaTime, 0, 1.f);
	// }
	// else
	// {
	// 	DrawDebugLine(GetWorld(), StartLocation, EndLocation, FColor::Red, false, DeltaTime, 0, 1.f);
	// }
}

AActor* UInteractionInteractorComponent::GetInterestActor()
{
	return InterestActor;
}

void UInteractionInteractorComponent::ClientInteractorStart(AActor* Interactor, AActor* Participant)
{
	if(Interactor == nullptr || Participant == nullptr)
	{
		return;
	}

	ServerInteractorStart(Interactor, Participant);
}

void UInteractionInteractorComponent::ClientInteractorStop(AActor* Interactor, AActor* Participant)
{
	if(Interactor == nullptr || Participant == nullptr)
	{
		return;
	}

	ServerInteractorStart(Interactor, Participant);
}

void UInteractionInteractorComponent::ServerInteractorStart_Implementation(AActor* Interactor, AActor* Participant)
{
	if(Interactor == nullptr || Participant == nullptr)
	{
		UE_LOG(::LogProcess, Error, TEXT("Server get null object"));
	}
	UInteractionParticipantComponent* ParticipantComponent = Participant->GetComponentByClass<UInteractionParticipantComponent>();
	if(ParticipantComponent == nullptr)
	{
		UE_LOG(::LogProcess, Error, TEXT("ParticipantComponent is null"));
	}
	if(NotifyInteractorStartScript.IsBound())
	{
		NotifyInteractorStartScript.Execute(Interactor, Participant);
	}
	ParticipantComponent->ServerParticipantStart(Interactor, Participant);
}

void UInteractionInteractorComponent::ServerInteractorStop_Implementation(AActor* Interactor, AActor* Participant)
{
	if(Interactor == nullptr || Participant == nullptr)
	{
		UE_LOG(::LogProcess, Error, TEXT("Server get null object"));
	}
	UInteractionParticipantComponent* ParticipantComponent = Participant->GetComponentByClass<UInteractionParticipantComponent>();
	if(ParticipantComponent == nullptr)
	{
		UE_LOG(::LogProcess, Error, TEXT("ParticipantComponent is null"));
	}
	if(NotifyInteractorStopScript.IsBound())
	{
		NotifyInteractorStopScript.Execute(Interactor, Participant);
	}
	ParticipantComponent->ServerParticipantStop(Interactor, Participant);
}
